﻿using System;
using System.Collections.Generic;
using System.Collections;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Media3D;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Input;
using Bling.DSL;
using Bling.D3D;
using Bling.Arrays;
using Bling.Properties;
using Bling.Util;
using Bling.Physics;
using Bling.WPF;
using Bling.Shapes;
using Bling.Animation;

namespace Bling.Physics {
  /// <summary>
  /// Creates a world, canvas, and driver for a physical simulation.
  /// </summary>
  public abstract class PhysicsCanvas : Canvas, CanStart {
    private readonly World World;
    private static int run = 0;
    private readonly Bling.Physics.Gen.WorldDriver Driver;

    private static readonly GetProperty<PhysicsCanvas, Point> ShakeProperty = "Shake".NewProperty<PhysicsCanvas, Point>();
    public PointBl Shake {
      get { return ShakeProperty[this]; }
      set { Shake.Bind = value; }
    }

    /// <summary>
    /// Create a new physics canvas.
    /// </summary>
    /// <param name="Name">Name of world to create.</param>
    /// <param name="millis">Update time in milliseconds.</param>
    public PhysicsCanvas(string Name, int millis) {
      World = new World() {
        Name = Name + (run++)
      };
      var Post = Init(this, World);
      Driver = new Bling.Physics.Gen.WorldDriver(World, Dispatcher, millis);
      Post();
    }
    /// <summary>
    /// Initialize constraints of physical canvas.
    /// </summary>
    /// <param name="MainCanvas">This canvas.</param>
    /// <param name="World">Created physics world.</param>
    /// <returns>Action to call after code is generated: specify if you need to set physical properties directly from UI code.</returns>
    protected abstract Action Init(CanvasBl MainCanvas, World World);
    public void Start() {
      Driver.Start(); 
    }
    public void Stop() { Driver.Stop(); }
  }
  public class CurtainUI {
    public PointBl Consume = new PointBl(1,1);
    public DoubleBl Open = 1d;
    public Func<DoubleBl> OpenZ;
    public bool DoRight = false; 
    public bool PinTop = false;
    public bool PinSide = false;
    public bool AddTextBox = false;
    public BrushBl Background = Brushes.Red;
    public Viewport3DBl Viewport { get; private set; }
    public CanvasBl Cloth { get; private set; }
    public PointBl MeshSize { get; private set; }
    private int RowCount;
    private int ColumnCount;
    private GridMesh clothMesh;
    public CurtainUI() {
      this.OpenZ = () => (1d - Open) * 0.5;
    }

    public void Activate<BLOCK>(PhysicsBlock<BLOCK> block)
      where BLOCK : PhysicsBlock<BLOCK>, MeshBody.Is<BLOCK>, Position3DBody.Is<BLOCK> {
      var array = block.PositionBodyE().PositionArray(block).Horizontal2D(block.ColumnCount());

      // var array = block.Position3DArray().Horizontal2D(ColumnCount);
      // flip Y because Y grows up in 3D instead of down.
      array = array.Map((i, j, p) => {
        PointBl orig = block.MeshBodyE().LeftTop + new PointBl(i.Bl(), j.Bl()) * block.MeshBodyE().CellSize;
        orig = new PointBl(orig.X, MeshSize.Y - orig.Y);
        p = new Point3DBl(p.Bl().X, MeshSize.Y - p.Bl().Y, p.Bl().Z);
        var x = p.Bl().X;
        var y = p.Bl().Y;
        p = new Point3DBl(x, y, p.Bl().Z);
        return p;
      });
      if (PinSide) {
        throw new NotSupportedException("Not yet");
      }
      // likewise, reverse the rows because Y grows up. 
      array = array.ReverseRows;
      if (PinTop) {
        // splice in a last row to pin the curtain to the top
        var lastRow = new Array2D<Point3D>(array.ColumnCount, 1, (i, j) => {
          return new Point3DBl(i * block.MeshBodyE().CellSize.X + block.MeshBodyE().LeftTop.X, 1d, 0);
        });
        // since block has only RowCount - 1 rows, the resulting array has RowCount rows.
        array = array.SpliceRows(array.RowCount, lastRow);
      }

      var closeValue = OpenZ(); // .5; // (1 - Open) * .5;
      array = array.Map((i, j, p) => {
         var x = p.Bl().X;
        var y = p.Bl().Y;
        return new Point3DBl(x, y + (j.Bl() == 0 & i.Bl() % 3 > 0).Condition(0.016d.Bl(), 0), p.Bl().Z + (i.Bl() % 3 > 0).Condition(closeValue, 0));
      });
      // Refresh in UI on each time step.
      block.World.UpdateInUI.Add(clothMesh.Refresh(array));
    }

    public void Init<BLOCK>(PhysicsBlock<BLOCK> block, CanvasBl Canvas)
      where BLOCK : PhysicsBlock<BLOCK>, MeshBody.Is<BLOCK>, PositionBody.Is<BLOCK> {
      this.RowCount = block.RowCount() + (PinTop ? 1 : 0);
      this.ColumnCount = block.ColumnCount() + (PinSide ? 1 : 0);

      DoubleBl ratio = (Canvas.Size.X * Consume.X / (Canvas.Size.Y * Consume.Y).Max(1));
      this.MeshSize = new PointBl(ratio, 1d);
      {
        PointBl UseLeftTop = new PointBl(0, 0);
        if (PinTop) UseLeftTop += new PointBl(0d, 1d / (block.RowCount() + 1d));
        if (PinSide ) UseLeftTop += new PointBl((!DoRight).Bl().Condition(1d / (block.ColumnCount() + 1d), 0d), 0d);

        PointBl UseMeshSize = (MeshSize - UseLeftTop) * new PointBl(Open, 1d);
        block.MeshBodyE().LeftTop = UseLeftTop
          + new PointBl(DoRight.Bl().Condition((MeshSize - UseLeftTop).X * (1d - Open), 0d), 0);
        block.MeshBodyE().Size = UseMeshSize;
      }
      this.clothMesh = new GridMesh(ColumnCount, RowCount);
      this.Cloth = new CanvasBl() {
        Background = Background,
        Size = Canvas.Size * Consume,
      };
      if (AddTextBox) {
        var box = new TextBoxBl(Canvas) {
          LeftBottom = new PointBl(DoRight.Bl().Condition(Canvas.Width * (1d - Consume.X), 0), Canvas.Height - 20d),
          Width = Canvas.Width * 0.5, ZIndex = 100,
          Font = { Size = 40, Weight = FontWeights.SemiBold, /*Family = "Arial".Convert<FontFamily>(),*/ },
          Background = Brushes.Transparent, Foreground = Brushes.Transparent,
        };
        var box2 = new TextBlockBl(this.Cloth) {
          Text = box.Text, Font = box.Font,
          LeftTop = box.LeftTop, Size = box.Size,
          Foreground = Brushes.Black,
        };

        //Action<int, int> doSomething = Point3DBl.Assign<int, int>((i, j) => block[i].Position3D(), (i, j) => block[i].Position3D() + new Point3DBl(.0, .0, -0.2));

        var cbox = box.CurrentValue;
        cbox.TextChanged += (x, y) => {
          if (y.Changes.Count == 1) {
            foreach (var change in y.Changes) {
              if ((change.AddedLength == 1 && change.RemovedLength == 0)
              || (change.AddedLength == 0 && change.RemovedLength == 1)) {
                var xPos = cbox.GetRectFromCharacterIndex(cbox.Text.Length).Left;

                var pos = new PointBl(xPos, Canvas.Height);
                pos = pos / Canvas.Size;
                pos = pos * MeshSize;
                //int row = (int)(pos.Y / y_step).CurrentValue;
                //row = (row >= RowsN) ? RowsN - 1 : row;
                var x_step = MeshSize.X / (ColumnCount - 1);
                for (int row = 0; row < RowCount - 1; row++) {
                  int col = (int)(pos.X * 2.0 / x_step).CurrentValue;
                  col = Math.Min(col, ColumnCount - 1);
                 // doSomething(row, col);
                }
              }
            }
          }
        };
      }

      this.Viewport = new Viewport3DBl(Canvas) {
        LeftTop = new PointBl(0, 0) + new PointBl(DoRight.Bl().Condition(Canvas.Width * (1d - Consume.X), 0), 0),
        ZIndex = 0,
        Size = Canvas.Size * Consume,
        Visibility = true,
        FlatPerspective = MeshSize,
        Children = {
          AddMany = new Visual3DBl[] { 
            new Viewport2DVisual3DBl() {
              Material = new DiffuseMaterialBl() { IsVisualHostMaterial = true, },
              CachingHint = CachingHint.Cache,
              CacheInvalidationThreshold = { Minimum = 0.001, Maximum = 1000, },
              Visual = this.Cloth,
              Geometry = clothMesh,
            },
            new ModelVisual3DBl() {
              Content = new SpotLightBl() {
                Color = Colors.White,
                Position = new Point3DBl(DoRight.Bl().Condition(0.0, MeshSize.X), MeshSize.Y*1.0, 0.8),
                Direction = new Point3DBl(0, -0.7, -0.7),
                InnerConeAngle = 30d.ToDegrees(),
                OuterConeAngle = 60d.ToDegrees(),
              },
            },
          },
        },
      };
    }
  }
  public static class PhysicsUIExtensions {
    public static PlainBlock<PathShape, PathShapeBl> MakeLineGrid<BLOCK>(this PhysicsBlock<BLOCK> block, CanvasBl Canvas, PointBl MeshSize, Point4DBl Bounds, Action<IntBl,IntBl,PathShapeBl> InitF)
      where BLOCK : PhysicsBlock<BLOCK>, MeshBody.Is<BLOCK>, PositionBody.Is<BLOCK> {

      var shapes = new PlainBlock<PathShape, PathShapeBl>(block.World, (block.ColumnCount() - 1) * (block.RowCount() - 1), (idx) =>
        new PathShapeBl(Canvas) {});

      for (int i = 0; i < block.ColumnCount() - 1; i++) {
        for (int j = 0; j < block.RowCount() - 1; j++) {
          InitF(i, j, shapes[j * (block.ColumnCount() - 1) + i].At);
        }
      }

      var segments = new PlainBlock<LineSegment, LineSegmentBl>(block.World, shapes.Count * 4, (idx) =>
        new LineSegmentBl() { });
      for (int i = 0; i < shapes.Count; i++) {
        shapes[i].At.Segments.AddMany = new PathSegmentBl[] {
          segments[i*4 + 0].At,
          segments[i*4 + 1].At,
          segments[i*4 + 2].At,
          segments[i*4 + 3].At,
        };
      }
      Func<PointBl, PointBl> F = q => {
        q = (q / MeshSize * Bounds.Size + Bounds.LeftTop);
        return q;
      };

      shapes.ForAll((shape) => {
        var i = shape.Index % (block.ColumnCount() - 1);
        var j = shape.Index / (block.ColumnCount() - 1);
        var body = block.Get(i,j);
        var p = block.PositionBodyE().PositionFor(body);
        F(p.XY).Link.To = shape.At.Start;
      });
      segments.ForAll((segment) => {
        var kA = segment.Index / 4;
        var kB = segment.Index % 4;
        var i = kA % (block.ColumnCount() - 1);
        var j = kA / (block.ColumnCount() - 1);
        F(block.PositionBodyE().PositionFor(block.Get(i + (kB <= 1).Condition(1, 0), j + (kB >= 1 & kB <= 2).Condition(1, 0))).XY).Link.To = 
          segment.At.Point;
      });
      return shapes;
    }
    public static PlainBlock<Thumb, ThumbBl> MakeThumbGrid<BLOCK>(this PhysicsBlock<BLOCK> block, CanvasBl Canvas, 
      PointBl MeshSize, Point4DBl bounds, Action<IntBl, IntBl, ThumbBl> InitF)
      where BLOCK : PhysicsBlock<BLOCK>, MeshBody.Is<BLOCK>, PositionBody.Is<BLOCK> {
      var thumbs = new PlainBlock<Thumb, ThumbBl>(block.World, block.Count, (ij) => {
        var ret = new ThumbBl(Canvas);
        InitF(ij % block.ColumnCount(), ij / block.ColumnCount(), ret);
        return ret;
      });
      PlainBlock<Slider, SliderBl> sliders = null;
      if (block.PositionBodyE().TypeOfPoint == typeof(System.Windows.Media.Media3D.Point3D)) {
        sliders = new PlainBlock<Slider, SliderBl>(block.World, block.Count, (ij) => new SliderBl());
      }
      thumbs.ForAll(thumb => {
        var p = block.PositionBodyE().PositionFor(block[thumb.Index]);
        if (p is Point3DBl) {
          var z = sliders[thumb.Index].At.Value;
          var q = (Point3DBl)p;
          q = q * new Point3DBl(bounds.Size / MeshSize, 1d);
          q = q + new Point3DBl(bounds.LeftTop, 0d);
          q.Link[!thumb.At.IsDragging] = new Point3DBl(thumb.At.CenterPosition, z);
        } else if (p is PointBl) {
          var q = (PointBl)p;
          q = (q / MeshSize * bounds.Size + bounds.LeftTop);
          q.Link[!thumb.At.IsDragging] = thumb.At.CenterPosition;
        }
      });
      return thumbs;
    }
    public static void ConnectBlock<BLOCK, T, BRAND>(this PhysicsBlock<T, BRAND, BLOCK> block)
      where BLOCK : PhysicsBlock<T, BRAND, BLOCK>, ShapeBody.Is<BLOCK>, ParticleBody.Is<BLOCK>, RigidBody.Is<BLOCK>
      where T : FrameworkElement
      where BRAND : FrameworkElementBl<T, BRAND> {
      block.ConnectBlock(i => block.InitCenter());
    }


    /// <summary>
    /// Connect widget wrapped by body to shape body center and rotation physical properties. 
    /// </summary>
    public static void ConnectBlock<BLOCK, T, BRAND>(this PhysicsBlock<T, BRAND, BLOCK> block, Func<IntBl,PointBl> CenterFor)
      where BLOCK : PhysicsBlock<T, BRAND, BLOCK>, /*ShapeBody.Is<BLOCK>, ParticleBody.Is<BLOCK>,*/ RigidBody.Is<BLOCK>
      where T : FrameworkElement
      where BRAND : FrameworkElementBl<T, BRAND> {
      for (int i = 0; i < block.Count; i++) {
        var center = CenterFor(i); // block.RigidBodyE().CenterOfTransform(block.Get(i).Bl().Size);
        block.Get(i).Bl().EnableAtRotation = center;
        block.Get(i).Bl().EnableAtScale = center;
      }
      block.ForAll(body => {
        //(body.Rotation().Inverse - .5.ToPI()).Link.To = body.At.AtRotation.ToRadians();
        (body.Rotation()).Link.To = body.At.AtRotation.ToRadians();
        body.Center().Link.To = (body.At.LeftTop + CenterFor(body.Index));
        DoubleBl scale = body.Scale(); 
        new PointBl(scale, scale).Link.To = body.At.AtScale;
      });
    }
  }
  public static class ImmediateRender {
    public static Impl<BLOCK> ImmediateRenderE<BLOCK>(this ImmediatePhysicsBlock<BLOCK> block)
      where BLOCK : ImmediatePhysicsBlock<BLOCK>, Is<BLOCK> {
      return block.Extension<Impl<BLOCK>>();
    }
    public static DoubleBl Radius<BLOCK>(this ImmediatePhysicsBlock<BLOCK>.Body body)
      where BLOCK : ImmediatePhysicsBlock<BLOCK>, Is<BLOCK> {
      return body.GetProperty(body.Block.ImmediateRenderE().RadiusProperty);
    }

    public static PointBl CenterX<BLOCK>(this ImmediatePhysicsBlock<BLOCK>.Body body)
      where BLOCK : ImmediatePhysicsBlock<BLOCK>, Is<BLOCK> {
      return body.GetProperty(body.Block.ImmediateRenderE().CenterProperty);
    }
    public static PointBl LeftTop<BLOCK>(this ImmediatePhysicsBlock<BLOCK>.Body body)
      where BLOCK : ImmediatePhysicsBlock<BLOCK>, Is<BLOCK> {
      return body.CenterX() - new PointBl(1d, 1d) * body.Radius();
    }
    public static PointBl RightBottom<BLOCK>(this ImmediatePhysicsBlock<BLOCK>.Body body)
      where BLOCK : ImmediatePhysicsBlock<BLOCK>, Is<BLOCK> {
      return body.CenterX() + new PointBl(1d, 1d) * body.Radius();
    }

    public static BrushBl Brush<BLOCK>(this ImmediatePhysicsBlock<BLOCK>.Body body)
      where BLOCK : ImmediatePhysicsBlock<BLOCK>, Is<BLOCK> {
      return body.GetProperty(body.Block.ImmediateRenderE().BrushProperty);
    }

    public interface Is<BLOCK> : HasBlockExtension<BLOCK, Impl<BLOCK>>
      where BLOCK : ImmediatePhysicsBlock<BLOCK>, Is<BLOCK> { }


    public class Impl<BLOCK> : BlockExtension<Impl<BLOCK>, BLOCK> where BLOCK : ImmediatePhysicsBlock<BLOCK>, Is<BLOCK> {
      public readonly CustomRender Render = new CustomRender();
      public class CustomRender : FrameworkElement {
        internal Action<DrawingContext> Render;
        protected override void OnRender(DrawingContext dc) {
          base.OnRender(dc);
          if (Render != null)
            Render(dc);
        }
      }
      internal readonly PhysicsProperty<Point> CenterProperty;
      internal readonly PhysicsProperty<double> RadiusProperty;
      internal readonly PhysicsProperty<Brush> BrushProperty;
      public Impl(BLOCK block, CanvasBl MainCanvas)
        : base(block) {
        CenterProperty = block.NewBodyProperty<Point>("Center");
        RadiusProperty = block.NewBodyProperty<double>("Radius");
        BrushProperty = block.NewBodyProperty<Brush>("Brush");
        Render.Bl().LeftTop = new PointBl(0, 0);
        Render.Bl().Size = MainCanvas.Size;
        MainCanvas.CurrentValue.Children.Add(Render);
        block.World.UpdateInUI.Add(() => Render.InvalidateVisual());
      }
      protected internal override void DoInit(BLOCK block) {
        block.ForAll(body => {
          body.Brush().Init = Brushes.Red;
          body.Radius().Init = 100d;
          body.CenterX().Init = new PointBl(0, 0);
        });
      }
      protected internal override void DoActivate(BLOCK block) {
        base.DoActivate(block);
        var CenterF = PointBl.Func<int>(i => block[i].CenterX());
        var RadiusF = DoubleBl.Func<int>(i => block[i].Radius());
        var ColorF = BrushBl.Func<int>(i => block[i].Brush());
        Render.Render = (dc) => {
          for (int i = 0; i < (block.Count); i++) {
            var r = RadiusF(i);
            dc.DrawEllipse(ColorF(i), null, CenterF(i), r, r);
          }
        };
      }
    }
  }

  /// <summary>
  /// Extends a block with mouse-based input (the mouse acts as a joint). 
  /// </summary>
  public static class MouseAdapter {
    /// <summary>
    /// Return if the specified body is pinned by the mouse.
    /// </summary>
    public static BoolBl IsPinned<T, BLOCK>(this PhysicsBlock<T,BLOCK>.Body body) 
      where BLOCK : PhysicsBlock<T,BLOCK>, Is<T,BLOCK>, ParticleBody.Is<BLOCK> where T : FrameworkElement {
      return body.GetProperty(body.Block.MouseAdapterE().IsPinnedProperty);
    }
    /// <summary>
    /// Return mouse adapeter extension for this block.
    /// </summary>
    public static Impl<T,BLOCK> MouseAdapterE<T, BLOCK>(this PhysicsBlock<T, BLOCK> block)
      where BLOCK : PhysicsBlock<T, BLOCK>, Is<T,BLOCK>, ParticleBody.Is<BLOCK>
      where T : FrameworkElement {
      return block.Extension<Impl<T,BLOCK>>();
    }
    public static void MakeMouseAdapterE<T, BLOCK>(this PhysicsBlock<T, BLOCK> block)
      where BLOCK : PhysicsBlock<T, BLOCK>, Is<T, BLOCK>, ParticleBody.Is<BLOCK>
      where T : FrameworkElement {
      new Impl<T, BLOCK>((BLOCK) block);
    }

    /// <summary>
    /// A block extends this if they implement the containing extension.
    /// </summary>
    public interface Is<T, BLOCK> : HasBlockExtension<BLOCK, Impl<T,BLOCK>>
      where BLOCK : PhysicsBlock<T, BLOCK>, Is<T,BLOCK>, ParticleBody.Is<BLOCK>
      where T : FrameworkElement { }
    /// <summary>
    /// State for containing extension.
    /// </summary>
    public class Impl<T, BLOCK> : BlockExtension<Impl<T,BLOCK>, BLOCK>
      where BLOCK : PhysicsBlock<T, BLOCK>, Is<T,BLOCK>, ParticleBody.Is<BLOCK>
      where T : FrameworkElement {
      internal readonly PhysicsProperty<bool> IsPinnedProperty;
      public FrameworkElement ShowMove = null;

      public Impl(BLOCK block) : base(block) {
        IsPinnedProperty = block.NewBodyProperty<bool>("IsPinned");
        Click = (body,idx) => { };
        Flick = (body,idx) => { };
        Init = (body, idx) => { };
        End = (body, idx) => { };
      }
      protected internal override void DoInit(BLOCK block) {
        block.ForAll((PhysicsBlock<T, BLOCK>.Body body) => { 
          MouseAdapter.IsPinned<T,BLOCK>(body).Init = false; 
        });
      }
      public Action<PhysicsBlock<T, BLOCK>.Body,int> Click { set; private get; }
      public Action<PhysicsBlock<T, BLOCK>.Body, int> Flick { set; private get; }
      public Action<PhysicsBlock<T, BLOCK>.Body, int> Init { set; private get; }
      public Action<PhysicsBlock<T, BLOCK>.Body, int> End { set; private get; }


      protected internal override void DoActivate(BLOCK block) {
        base.DoActivate(block);
        var ParticlesCount = ParticleBody.Impl<BLOCK>.ParticlesCount;
        Action<int>[] SetPoints = new Action<int>[ParticlesCount];
        Func<int, Point>[] GetPoints = new Func<int, Point>[ParticlesCount];
        var Lengths0 = new double[ParticlesCount];

        Action<int> SetPinned = BoolBl.Assign<int>(i => IsPinned<T,BLOCK>(block[i]), i => true);
        Action<int> ResetPinned = BoolBl.Assign<int>(i => IsPinned<T,BLOCK>(block[i]), i => false);

        var MousePosition = block[0].At.Bl0().CanvasParent.Underlying.Map<Point>(c => { 
          var ret = Mouse.GetPosition(c);
          return ret;
        }).Bl();
        {
          var Lengths = new DoubleBl[ParticlesCount];
          for (int i = 0; i < ParticlesCount; i++) {
            var ii = i;
            Lengths[ii] = Expr<double>.FromFunc(() => Lengths0[ii]);

            GetPoints[i] = PointBl.Func<int>(j => block[j].Particles()[ii]);
            SetPoints[i] = PointBl.Assign<int>((j) => block[j].Particles()[ii], (j) => {
              return block[j].Particles()[ii].Position.Spring(MousePosition, Lengths[ii]).
                Check("XX: ".Bl() + MousePosition + " " + block[j].Particles(ii).Position);
            });
          }
        }
        if (ShowMove != null) ShowMove.Bl().Visibility = false;
        for (int i = 0; i < block.Count; i++) {
          var ii = i;
          block[i].At.Bl0().OnMouseDown = (x, y) => {
            if (ShowMove != null) {
              ShowMove.Bl().Visibility = true;
              ShowMove.Bl().CenterPosition.Now = MousePosition;
            }

            Vector downPoint = (Vector)MousePosition.CurrentValue;
            Init(block[ii], ii);
            SetPinned(ii);
            for (int j = 0; j < ParticlesCount; j++)
              Lengths0[j] = ((Vector)(GetPoints[j](ii) - downPoint)).Length;

            Mouse.Capture(x.CurrentValue, CaptureMode.Element);
            Action Move = () => {
              for (int j = 0; j < ParticlesCount; j++) 
                SetPoints[j](ii);
              if (ShowMove != null) 
                ShowMove.Bl().CenterPosition.Now = MousePosition;
            };
            block.World.UpdateInUI.Add(Move);
            x.OnMouseUp = (x0, y0) => {
              if (ShowMove != null) ShowMove.Bl().Visibility = false;
              ResetPinned(ii);
              Mouse.Capture(x0.CurrentValue, CaptureMode.None);
              End(block[ii], ii);
              block.World.UpdateInUI.Remove(Move);
              Vector upPoint = (Vector)MousePosition.CurrentValue;
              downPoint = Clean(downPoint);
              upPoint = Clean(upPoint);
              if (upPoint == downPoint) Click(block[ii], ii);
              else Flick(block[ii], ii);
              return false;
            };
            return true;
          };
        }
      }
      private Vector Clean(Vector v) {
        return new Vector(Math.Round(v.X / 1.1) * 1.1, Math.Round(v.Y / 1.1) * 1.1);
      }
    }
  }
  /// <summary>
  /// Extends a block with mouse-based input (the mouse acts as a joint). 
  /// </summary>
  public static class PositionMouseAdapter {
    /// <summary>
    /// Return if the specified body is pinned by the mouse.
    /// </summary>
    public static BoolBl IsPinnedX<T, BLOCK>(this PhysicsBlock<T, BLOCK>.Body body)
      where BLOCK : PhysicsBlock<T, BLOCK>, Is<T, BLOCK>, PositionBody<Point, PointBl>.Is<BLOCK>
      where T : FrameworkElement {
      return body.GetProperty(body.Block.PositionMouseAdapterE().IsPinnedProperty);
    }
    /// <summary>
    /// Return mouse adapeter extension for this block.
    /// </summary>
    public static Impl<T, BLOCK> PositionMouseAdapterE<T, BLOCK>(this PhysicsBlock<T, BLOCK> block)
      where BLOCK : PhysicsBlock<T, BLOCK>, Is<T, BLOCK>, PositionBody<Point, PointBl>.Is<BLOCK>
      where T : FrameworkElement {
      return block.Extension<Impl<T, BLOCK>>();
    }

    /// <summary>
    /// A block extends this if they implement the containing extension.
    /// </summary>
    public interface Is<T, BLOCK> : HasBlockExtension<BLOCK, Impl<T, BLOCK>>
      where BLOCK : PhysicsBlock<T, BLOCK>, Is<T, BLOCK>, PositionBody<Point, PointBl>.Is<BLOCK>
      where T : FrameworkElement { }
    /// <summary>
    /// State for containing extension.
    /// </summary>
    public class Impl<T, BLOCK> : BlockExtension<Impl<T, BLOCK>, BLOCK>
      where BLOCK : PhysicsBlock<T, BLOCK>, Is<T, BLOCK>, PositionBody<Point, PointBl>.Is<BLOCK>
      where T : FrameworkElement {
      internal readonly PhysicsProperty<bool> IsPinnedProperty;
      public FrameworkElement ShowMove = null;

      public Impl(BLOCK block)
        : base(block) {
        IsPinnedProperty = block.NewBodyProperty<bool>("IsPinned");
        Click = (body, idx) => { };
        Flick = (body, idx) => { };
        Init = (body, idx) => { };
        End = (body, idx) => { };
      }
      protected internal override void DoInit(BLOCK block) {
        block.ForAll((PhysicsBlock<T, BLOCK>.Body body) => {
          body.IsPinnedX().Init = false;
        });
      }
      public Action<PhysicsBlock<T, BLOCK>.Body, int> Click { set; private get; }
      public Action<PhysicsBlock<T, BLOCK>.Body, int> Flick { set; private get; }
      public Action<PhysicsBlock<T, BLOCK>.Body, int> Init { set; private get; }
      public Action<PhysicsBlock<T, BLOCK>.Body, int> End { set; private get; }


      protected internal override void DoActivate(BLOCK block) {
        base.DoActivate(block);
        Action<int>[] SetPoints = new Action<int>[1];
        Func<int, Point>[] GetPoints = new Func<int, Point>[1];
        var Lengths0 = new Point[1];

        Action<int> SetPinned = BoolBl.Assign<int>(i => IsPinnedX<T, BLOCK>(block[i]), i => true);
        Action<int> ResetPinned = BoolBl.Assign<int>(i => IsPinnedX<T, BLOCK>(block[i]), i => false);

        var MousePosition = block[0].At.Bl0().CanvasParent.Underlying.Map<Point>(c => {
          var ret = Mouse.GetPosition(c);
          return ret;
        }).Bl();
        {
          var Lengths = new PointBl[1];
          for (int i = 0; i < 1; i++) {
            var ii = i;
            Lengths[ii] = Expr<Point>.FromFunc(() => Lengths0[ii]);

            GetPoints[i] = PointBl.Func<int>(j => block[j].Position());
            SetPoints[i] = PointBl.Assign<int>((j) => block[j].Position(), (j) => {
              return MousePosition + Lengths[ii];

              //return block[j].Position().Spring(MousePosition, Lengths[ii]).
              //  Check("XX: ".Bl() + MousePosition + " " + block[j].Position());
            });
          }
        }
        if (ShowMove != null) ShowMove.Bl().Visibility = false;
        for (int i = 0; i < block.Count; i++) {
          var ii = i;
          block[i].At.Bl0().OnMouseDown = (x, y) => {
            if (ShowMove != null) {
              ShowMove.Bl().Visibility = true;
              ShowMove.Bl().CenterPosition.Now = MousePosition;
            }

            Vector downPoint = (Vector) MousePosition.CurrentValue;
            Init(block[ii], ii);
            SetPinned(ii);
            for (int j = 0; j < 1; j++)
              Lengths0[j] = (GetPoints[j](ii) - downPoint);

            Mouse.Capture(x.CurrentValue, CaptureMode.Element);
            Action Move = () => {
              for (int j = 0; j < 1; j++)
                SetPoints[j](ii);
              if (ShowMove != null)
                ShowMove.Bl().CenterPosition.Now = MousePosition;
            };
            block.World.UpdateInUI.Add(Move);
            x.OnMouseUp = (x0, y0) => {
              if (ShowMove != null) ShowMove.Bl().Visibility = false;
              ResetPinned(ii);
              Mouse.Capture(x0.CurrentValue, CaptureMode.None);
              End(block[ii], ii);
              block.World.UpdateInUI.Remove(Move);
              Vector upPoint = (Vector)MousePosition.CurrentValue;
              downPoint = Clean(downPoint);
              upPoint = Clean(upPoint);
              if (upPoint == downPoint) Click(block[ii], ii);
              else Flick(block[ii], ii);
              return false;
            };
            return true;
          };
        }
      }
      private Vector Clean(Vector v) {
        return new Vector(Math.Round(v.X / 1.1) * 1.1, Math.Round(v.Y / 1.1) * 1.1);
      }
    }
  }

}